/*
	The MIT License

	Copyright (c) 2010 IFMO/GameDev Studio

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.
*/

#pragma once
	
/*-----------------------------------------------------------------------------
	MATRIX.H
-----------------------------------------------------------------------------*/

#define MATRIX_INVERSE_EPSILON		1e-14

class EMatrix	{
	public:
						EMatrix			( void );
						EMatrix			( const EMatrix & other );
						EMatrix			(	float a00, float a01, float a02, float a03, 
											float a10, float a11, float a12, float a13,
											float a20, float a21, float a22, float a23,
											float a30, float a31, float a32, float a33 );
		explicit		EMatrix			( const float *floatptr );
						
		float			&operator	()	( uint row, uint col );
		float			operator	()	( uint row, uint col ) const;

		float			*Ptr			( void );
		const	float	*Ptr			( void ) const;
		
		EMatrix			&operator	+=	( const EMatrix &other );
		EMatrix			&operator	-=	( const EMatrix &other );
		EMatrix			&operator	*=	( const EMatrix &other );
		
		EMatrix			operator	+	( const EMatrix &other ) const;
		EMatrix			operator	-	( const EMatrix &other ) const;
		EMatrix			operator	*	( const EMatrix &other ) const;
		EMatrix			operator	*	( float s ) const;
		EMatrix			operator	/	( float s ) const;
					
		EMatrix			&operator	*=	( float s );
		EMatrix			&operator	/=	( float s );
		
		bool			operator	==	( const EMatrix &other ) const;
		bool			operator	!=	( const EMatrix &other ) const;
		
		bool			IsEqual			( const EMatrix &other, float eps ) const;

		EMatrix			&SetIdentity		( void );
		
		EMatrix			Inverse			( void ) const; 
		EMatrix			Transpose		( void ) const;
		EMatrix			AffineInverse	( void ) const; 
		
		void			ToAngles		( float &yaw, float &pitch, float &roll ) const;
		void			ToPose			( EPoint &origin, EQuaternion &orient ) const;
		
	public:
		static const	EMatrix			kIdentity;
		
	public:
		static EMatrix	FromAngles			( float yaw, float pitch, float roll );
		static EMatrix	FromPose			( const EPoint &origin, const EQuaternion &orient );	
											
		static EMatrix	Translate			( float x, float y, float z );
		static EMatrix	Translate			( const EVector &dir );
		static EMatrix	Scale				( float x, float y, float z );
		static EMatrix	RotateX				( float a );
		static EMatrix	RotateY				( float a );
		static EMatrix	RotateZ				( float a );
		static EMatrix	PerspectiveRH		( float w, float h, float zn, float zf );
		static EMatrix	PerspectiveLH		( float w, float h, float zn, float zf );
		static EMatrix	PerspectiveFovRH	( float fovy, float aspect, float zn, float zf );	
		static EMatrix	PerspectiveFovLH	( float fovy, float aspect, float zn, float zf );	
		static EMatrix	OrthoRH				( float w, float h, float zn, float zf );
		static EMatrix	OrthoLH				( float w, float h, float zn, float zf );
		static EMatrix	OrthoOffCenterRH	( float l, float r, float b, float t, float zn, float zf );	
		static EMatrix	LookAtRH			( const EPoint &eye, const EPoint &at, const EVector &up );	
	
	protected:
		float data[4][4];
	};

/*-----------------------------------------------------------------------------
	MATRIX IMPLEMENTATION :
-----------------------------------------------------------------------------*/

inline EMatrix::EMatrix( void )
{
	SetIdentity();
}


inline EMatrix::EMatrix( const EMatrix & other )
{
	*this = other;
	//memcpy(data, other.Ptr(), sizeof(data));
}


inline EMatrix::EMatrix( float a00, float a01, float a02, float a03, 
				  float a10, float a11, float a12, float a13, 
				  float a20, float a21, float a22, float a23, 
				  float a30, float a31, float a32, float a33 )
{
	data[0][0] = a00;	data[0][1] = a01;	data[0][2] = a02,	data[0][3] = a03;
	data[1][0] = a10;	data[1][1] = a11;	data[1][2] = a12,	data[1][3] = a13;
	data[2][0] = a20;	data[2][1] = a21;	data[2][2] = a22,	data[2][3] = a23;
	data[3][0] = a30;	data[3][1] = a31;	data[3][2] = a32,	data[3][3] = a33;
}


inline EMatrix::EMatrix( const float *floatptr )
{
	memcpy(data, floatptr, sizeof(data));
}


inline float& EMatrix::operator()( uint row, uint col )
{
	return data[row][col];
}


inline float EMatrix::operator()( uint row, uint col ) const
{
	return data[row][col];
}


inline float* EMatrix::Ptr( void )
{
	return &data[0][0];
}


inline const float* EMatrix::Ptr( void ) const
{
	return &data[0][0];
}


inline EMatrix& EMatrix::operator+=( const EMatrix &other )
{
	for (int i=0; i<16; i++)	Ptr()[i] += other.Ptr()[i];
	return *this;
}

inline EMatrix& EMatrix::operator-=( const EMatrix &other )
{
	for (int i=0; i<16; i++)	Ptr()[i] -= other.Ptr()[i];
	return *this;
}

inline EMatrix& EMatrix::operator*=( const EMatrix &other )
{
	EMatrix temp	 = *this;

	for (register int i=0; i<4; i++)
		for (register int j=0; j<4; j++)
		{
			temp(i,j) = 
				data[i][0] * other(0,j) +
				data[i][1] * other(1,j) +
				data[i][2] * other(2,j) +
				data[i][3] * other(3,j);
		}

	*this	=	temp;

	return *this;
}

inline EMatrix& EMatrix::operator*=( float s )
{
	for (int i=0; i<16; i++)	Ptr()[i] *= s;
	return *this;
}

inline EMatrix EMatrix::operator+( const EMatrix &other ) const
{
	EMatrix temp(*this);
	temp += other;
	return temp;
}

inline EMatrix EMatrix::operator-( const EMatrix &other ) const
{
	EMatrix temp(*this);
	temp -= other;
	return temp;
}

inline EMatrix EMatrix::operator*( const EMatrix &other ) const
{
	EMatrix temp(*this);
	temp *= other;
	return temp;
}

inline EMatrix EMatrix::operator*( float s ) const
{
	EMatrix temp(*this);
	temp *= s;
	return temp;
}

inline EMatrix EMatrix::operator/( float s ) const
{
	EMatrix temp(*this);
	temp /= s;
	return temp;
}

inline EMatrix& EMatrix::operator/=( float s )
{
	for (int i=0; i<16; i++)	Ptr()[i] /= s;
	return *this;
}

inline bool EMatrix::operator==( const EMatrix &other ) const
{
	if (memcmp(this, &other, sizeof(EMatrix))==0) {
		return true;
	} else {
		return false;
	}
}

inline bool EMatrix::operator!=( const EMatrix &other ) const
{
	return !(*this==other);
}
